#!/bin/bash
# Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

# Reason for this no-op: shellcheck disable=... before the first command disables the error for the
# entire script.
:

# Disable unused variable error (needed to keep track of version)
# shellcheck disable=SC2034
CMK_VERSION="2.5.0b1"

# Informix
# Make ENV-VARs avail for subshells
set -a

#   .--helper--------------------------------------------------------------.
#   |                    _          _                                      |
#   |                   | |__   ___| |_ __   ___ _ __                      |
#   |                   | '_ \ / _ \ | '_ \ / _ \ '__|                     |
#   |                   | | | |  __/ | |_) |  __/ |                        |
#   |                   |_| |_|\___|_| .__/ \___|_|                        |
#   |                                |_|                                   |
#   '----------------------------------------------------------------------'

# BEGIN COMMON PLUGIN CODE

# check that no users other than root can change the file
only_root_can_modify() {
    permissions=$1
    owner=$2
    group=$3

    group_write_perm=$(echo "$permissions" | cut -c 6)
    other_write_perm=$(echo "$permissions" | cut -c 9)

    if [ "$owner" != "root" ] || [ "$other_write_perm" != "-" ]; then
        return 1
    fi

    [ "$group" = "root" ] || [ "$group_write_perm" = "-" ]
}

get_binary_owner() {
    BINARY_PATH=$1
    stat -c '%U' "${BINARY_PATH}"
}

get_binary_execution_mode() {
    BINARY_PATH=$1
    BINARY_USER=$2

    # if the executable belongs to someone besides root, do not execute it as root
    if needs_user_switch_before_executing "$BINARY_PATH"; then
        echo "su ${BINARY_USER} -c"
        return
    fi
    echo "bash -c"
}

needs_user_switch_before_executing() {
    BINARY_PATH=$1

    [ "$(whoami)" = "root" ] && ! only_root_can_modify "$(stat -c '%A' "$BINARY_PATH")" "$(stat -c '%U' "$BINARY_PATH")" "$(stat -c '%G' "$BINARY_PATH")"
}

# END COMMON PLUGIN CODE

set_env() {
    # set environment variables given in the form VARNAME1=value1;VARNAME2=value2;...
    while IFS=';' read -ra parts; do
        for part in "${parts[@]}"; do
            var_name="${part%%=*}"
            var_value="${part#*=}"
            export "$var_name"="$var_value"
        done
    done <<<"$1"
}

do_check() {
    # $1:section, $2:excludelist
    if echo "$2" | grep -qe "${1}"; then
        return 1
    else
        return 0
    fi
}

sql() {
    sqltxt="$1"
    export DBDELIMITER="|"
    echo "$sqltxt" | $EXECUTION_MODE "$INFORMIXDIR/bin/dbaccess sysmaster"
}

set_excludes() {
    exclude=""
    if [ "$EXCLUDES" = "ALL" ]; then
        exclude="$all_sections"
        global_exclude=true
    elif [ -n "$EXCLUDES" ]; then
        exclude=$EXCLUDES
        global_exclude=true
    else
        global_exclude=false
    fi

    if [ "$global_exclude" = "false" ]; then
        excludes_i="EXCLUDES_${1}"
        if [ "${!excludes_i}" = "ALL" ]; then
            exclude="$all_sections"
        elif [ -n "${!excludes_i}" ]; then
            exclude=${!excludes_i}
        fi
    fi
}

#.
#   .--sqls----------------------------------------------------------------.
#   |                                     _                                |
#   |                           ___  __ _| |___                            |
#   |                          / __|/ _` | / __|                           |
#   |                          \__ \ (_| | \__ \                           |
#   |                          |___/\__, |_|___/                           |
#   |                                  |_|                                 |
#   '----------------------------------------------------------------------'

all_sections="sessions locks tabextents dbspaces logusage"

informix_status() {
    echo "<<<informix_status:sep(58)>>>"
    echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
    $EXECUTION_MODE "\"$INFORMIXDIR\"/bin/onstat -" >/dev/null 2>&1
    state=$?
    echo "Status:"$state
    $EXECUTION_MODE "\"$INFORMIXDIR\"/bin/onstat -g dis"
    port=$(grep "$INFORMIXSERVER" /etc/services)
    echo "PORT:$port"
}

informix_sessions() {
    echo "<<<informix_sessions>>>"
    echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
    # don't count our own session
    sql "select 'SESSIONS', (count(*)-1)::int from syssessions"
}

informix_locks() {
    echo "<<<informix_locks>>>"
    echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
    # don't count our own session
    sql "select 'LOCKS', (count(*)-1)::int, type from syslocks group by type"
}

informix_tabextents() {
    echo "<<<informix_tabextents>>>"
    echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
    sql "select first 10
            'TABEXTENTS',
            trim(n.dbsname) db,
            trim(n.tabname) tab,
            h.nextns extents,
            nrows
        from sysptnhdr h, systabnames n
        where h.partnum = n.partnum
        and nrows > 0
        and n.dbsname not in ( 'sysadmin', 'sysuser', 'sysutils', 'sysmaster' )
        and n.tabname not like 'sys%'
        order by extents desc"
}

informix_dbspaces() {
    # the default dbspace pagesize must be taken to calculate the used and free space
    echo "<<<informix_dbspaces>>>"
    echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
    sql "select
            trim(sd.name) || ' DBSPACE',
            sd.dbsnum,
            sd.is_temp,
            sd.flags sd_flags,
            'CHUNK',
            sc.fname,
            (select sh_pagesize from sysmaster:sysshmvals) system_pagesize,
            sc.pagesize,
            sc.chksize,
            sc.nfree,
            sc.flags chunk_flags,
            trim(sc.mfname),
            sc.mflags
        from sysdbspaces sd, syschunks sc
        where sd.dbsnum = sc.dbsnum
        -- NO SBSPACE CURRENTLY
        and sd.is_sbspace = 0
        order by sd.name"
}

informix_logusage() {
    echo "<<<informix_logusage>>>"
    echo "[[[$INFORMIXSERVER/$SERVERNUM]]]"
    sql "select 'LOGUSAGE',
            number,
            sh_pagesize,
            size,
            used,
            flags,
            'is_used:'||is_used,
            'is_current:'||is_current,
            'is_backed_up:'||is_backed_up,
            'is_new:'||is_new,
            'is_archived:'||is_archived,
            'is_temp:'||is_temp,
            'is_pre_dropped:'||is_pre_dropped
        from syslogs, sysshmvals
        order by number"
}

#.
#   .--config--------------------------------------------------------------.
#   |                                      __ _                            |
#   |                      ___ ___  _ __  / _(_) __ _                      |
#   |                     / __/ _ \| '_ \| |_| |/ _` |                     |
#   |                    | (_| (_) | | | |  _| | (_| |                     |
#   |                     \___\___/|_| |_|_| |_|\__, |                     |
#   |                                           |___/                      |
#   '----------------------------------------------------------------------'

# Config opts:
# - oninit-path; Default is empty, which means autodetection:
#       ONINIT_PATH=<path to oninit-binary>
# - Excluding sections ("status sessions locks tabextents dbspaces logusage"):
#       EXCLUDES="SECTION SECTION ..."
#       EXCLUDES=ALL

# shellcheck source=agents/cfg_examples/informix.cfg
. "$MK_CONFDIR/informix.cfg" 2>/dev/null

if [ -z "$ONINIT_PATH" ] || [ ! -x "$ONINIT_PATH" ]; then
    ONINIT=$(UNIX95=true ps ax | grep oninit | grep -v grep | head -1 | awk '{print $1 " " $5}')
    if [ -z "$ONINIT" ]; then
        exit 0
    fi

    ONINIT_PATH=${ONINIT#* }
    ONINIT_PID=${ONINIT% *}
    case "$ONINIT_PATH" in
        /*) ;;

        *) # BUG not platform independent!
            ONINIT_PATH=$(readlink "/proc/$ONINIT_PID/exe")
            ;;
    esac

    # If not set in config or not found we end up here
    if [ -z "$ONINIT_PATH" ] || [ ! -f "$ONINIT_PATH" ]; then
        exit 1
    fi
fi

#.
#   .--main----------------------------------------------------------------.
#   |                                       _                              |
#   |                       _ __ ___   __ _(_)_ __                         |
#   |                      | '_ ` _ \ / _` | | '_ \                        |
#   |                      | | | | | | (_| | | | | |                       |
#   |                      |_| |_| |_|\__,_|_|_| |_|                       |
#   |                                                                      |
#   '----------------------------------------------------------------------'

INFORMIXDIR=${ONINIT_PATH%/bin*}

if [ ! -f "$INFORMIXDIR/bin/onstat" ]; then
    exit 1
fi

EXECUTION_MODE="$(get_binary_execution_mode "$INFORMIXDIR/bin/onstat" "$(get_binary_owner "$INFORMIXDIR/bin/onstat")")"

for IDSENV in $(
    $EXECUTION_MODE "$INFORMIXDIR/bin/onstat -g dis" |
        grep -E '^Server[         ]*:|^Server Number[     ]*:|^INFORMIX|^SQLHOSTS|^ONCONFIG' |
        sed -e 's/Server Number/SERVERNUM/' \
            -e 's/Server/INFORMIXSERVER/' \
            -e 's/SQLHOSTS/INFORMIXSQLHOSTS/' \
            -e 's/[     ]*:[    ]*/=/' |
        tr '\n' ';' |
        sed -e 's/;$/\n/' -e 's/;\(INFORMIXSERVER=[^;]*;\)/\n\1/g'

); do
    (
        set_env "$IDSENV"

        # try to set them via 'onstat -g env' otherwise
        # DB HAS TO BE RUNNING
        if [ -z "$INFORMIXSQLHOSTS" ] || [ -z "$ONCONFIG" ]; then
            $EXECUTION_MODE "$INFORMIXDIR/bin/onstat -g env" | grep -E -e '^INFORMIXSQLHOSTS' \
                -e '^ONCONFIG' |
                sed -e 's/[         ][      ]*/=/'
        fi

        informix_status

        set_excludes "$INFORMIXSERVER"

        if do_check "sessions" "$exclude"; then
            informix_sessions
        fi

        if do_check "locks" "$exclude"; then
            informix_locks
        fi

        if do_check "tabextents" "$exclude"; then
            informix_tabextents
        fi

        if do_check "dbspaces" "$exclude"; then
            informix_dbspaces
        fi

        if do_check "logusage" "$exclude"; then
            informix_logusage
        fi
    )
done
